
#include <wrapper.hpp>
using namespace xbase;

// sizeof(header_wrapper): 13
// sizeof(request_wrapper): 17
// sizeof(response_wrapper): 17
// sizeof(tablet_info_wrapper): 22
// sizeof(server_address_wrapper): 14
// sizeof(root_meta_req_wrapper): 53


struct header_min_wrapper	// 13
{
	integer_wrapper<uint16_t, kb::network_byteorder> m_magic;
	integer_wrapper<uint8_t, kb::network_byteorder> m_version;
	integer_wrapper<uint16_t, kb::network_byteorder> m_type;
	integer_wrapper<uint32_t, kb::network_byteorder> m_sequence_number;
	integer_wrapper<uint32_t, kb::network_byteorder> m_data_length;
};

struct header_wrapper		// 13
{
	integer_wrapper<uint16_t, kb::network_byteorder> m_magic;
	integer_wrapper<uint8_t, kb::network_byteorder> m_version;
	integer_wrapper<uint16_t, kb::network_byteorder> m_type;
	integer_wrapper<uint32_t, kb::network_byteorder> m_sequence_number;
	integer_wrapper<uint32_t, kb::network_byteorder> m_data_length;
};

struct request_wrapper		// 17 = 13 + 4
	: public header_wrapper
{
	integer_wrapper<uint32_t, kb::network_byteorder> m_program_version;
};

struct response_wrapper		// 17 = 13 + 4
	: public header_wrapper
{
	integer_wrapper<uint32_t, kb::network_byteorder> m_error_code;
};

struct tablet_info_wrapper	// 22
{
	// integer_wrapper<uint64_t, kb::network_byteorder> *m_tablet_id; // 8
	// string_wrapper<uint32_t, char, kb::network_byteorder> *m_table_name; // 4
	// string_wrapper<uint32_t, char, kb::network_byteorder> *m_start_row; // 4
	// string_wrapper<uint32_t, char, kb::network_byteorder> *m_end_row; // 4
	// integer_wrapper<uint16_t, kb::network_byteorder> *m_status; // 2

	integer_wrapper<uint64_t, kb::network_byteorder> *tablet_id() {
		return wrapper_cast<integer_wrapper<uint64_t, kb::network_byteorder>*>(this);
	}
	string_wrapper<uint32_t, char, kb::network_byteorder> *table_name() {
		return wrapper_cast<string_wrapper<uint32_t, char, kb::network_byteorder>*>(
			reinterpret_cast<char*>(this) + sizeof(uint64_t));
	}
	string_wrapper<uint32_t, char, kb::network_byteorder> *start_row() {
		return wrapper_cast<string_wrapper<uint32_t, char, kb::network_byteorder>*>(
			reinterpret_cast<char*>(this) + sizeof(uint64_t) + sizeof(uint32_t) + table_name()->size());
	}
	string_wrapper<uint32_t, char, kb::network_byteorder> *end_row() {
		return wrapper_cast<string_wrapper<uint32_t, char, kb::network_byteorder>*>(reinterpret_cast<char*>(this) + sizeof(uint64_t) + sizeof(uint32_t) + table_name()->size());
		
	}
	integer_wrapper<uint16_t, kb::network_byteorder> *status() {
		return wrapper_cast<integer_wrapper<uint16_t, kb::network_byteorder>*>(
			reinterpret_cast<char*>(this) + sizeof(uint64_t)
			+ sizeof(uint32_t) + table_name()->size()
			+ sizeof(uint32_t) + end_row()->size());
	}	
};

struct server_address_wrapper	// 14
{
	string_wrapper<uint32_t, char, kb::network_byteorder> *ip() {
		return wrapper_cast<string_wrapper<uint32_t, char, kb::network_byteorder>*>(this);
	}
	integer_wrapper<uint16_t, kb::network_byteorder> *port() {
		return wrapper_cast<integer_wrapper<uint16_t, kb::network_byteorder>*>(
			reinterpret_cast<char*>(this) + sizeof(int32_t) + ip()->size());
	}
	integer_wrapper<uint64_t, kb::network_byteorder> *startcode() {
		return wrapper_cast<integer_wrapper<uint64_t, kb::network_byteorder>*>(
			reinterpret_cast<char*>(this) + sizeof(int32_t) + ip()->size() + sizeof(uint16_t));
	}
};

struct root_meta_req_wrapper	// 53 = 17 + 22 + 14
	: public request_wrapper
{
	tablet_info_wrapper m_tablet_info;
	server_address_wrapper m_server_address;
};
	
char data[] = {
	// 1     2     3     4     5     6     7     8
	// header_wrapper : 13
	0x12, 0x34, /*magic*/
	0x56, /*version*/
	0x78, 0x90, /*type*/
	0xab, 0xcd, 0xef, 0x12, /*sequence_number*/
	0x34, 0x56, 0x78, 0x90, /*data_length*/

	// request_wrapper : 17
	0xab, 0xcd, 0xef, 0x12, /*program_version*/

	// tablet_info_wrapper : 
	0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, /*tablet_id*/  // 25
	0x00, 0x00, 0x00, 0x20, /*table_name:size*/ // 29
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*table_name:data*/ // 61
	0x00, 0x00, 0x00, 0x20, /*start_row:size*/ //65
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*start_row:data*/
	0x00, 0x00, 0x00, 0x20, /*end_row:size*/
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*end_row:data*/
	0xab, 0xcd, /*status*/

	// server_address_wrapper :
	0x00, 0x00, 0x00, 0x20, /*ip:size*/ //0x20 ~ 0x7E
	0x34, 0x32, 0x53, 0x34, 0x54, 0x21, 0x20, 0x7a,
	0x65, 0x69, 0x34, 0x32, 0x53, 0x34, 0x54, 0x21,
	0x20, 0x7a, 0x65, 0x69,	0x34, 0x32, 0x53, 0x34,
	0x54, 0x21, 0x20, 0x7a, 0x65, 0x69, 0x34, 0x4e,	/*ip:data*/
	0x78, 0x90, /*port*/
	0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, /*startcode*/

	// unused data
	0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef,
	0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef // 8
	// 8 x 8 = 64 bytes
};

#include <iostream>
#include <string>
#include <vector>
using namespace std;

// arg-list: "table name" "start row" "end row" "ip"
int main (int argc, char**argv)
{
	cout << "sizeof(header_wrapper): " << sizeof(header_wrapper) << endl;
	cout << "sizeof(request_wrapper): " << sizeof(request_wrapper) << endl;
	cout << "sizeof(response_wrapper): " << sizeof(response_wrapper) << endl;
	cout << "sizeof(tablet_info_wrapper): " << sizeof(tablet_info_wrapper) << endl;
	cout << "sizeof(server_address_wrapper): " << sizeof(server_address_wrapper) << endl;
	cout << "sizeof(root_meta_req_wrapper): " << sizeof(root_meta_req_wrapper) << endl;

	root_meta_req_wrapper *req = wrapper_cast<root_meta_req_wrapper*>(data);

	if (argc > 1) {		// has table name
		std::string table_name(argv[1]);
		if (table_name.size() > 32) {
			table_name = table_name.substr(0, 32);
		}
		while(table_name.size() < 32) {
			table_name += '-';
		}
		req->m_tablet_info.table_name()->fill(table_name);
	}
	if (argc > 2) {		// has start row
		std::string start_row(argv[2]);
		if (start_row.size() > 32) {
			start_row = start_row.substr(0, 32);
		}
		while(start_row.size() < 32) {
			start_row += '-';
		}
		req->m_tablet_info.start_row()->fill(start_row.data(),
						     start_row.size());
	}
	if (argc > 3) {		// has end row
		char buf[32];
		memset(buf, '-', 32);
		char *end_row = argv[3];
		unsigned int size = 0;
		while(*end_row && size < 32) {
			buf[size] = *end_row;
			++end_row; ++size;
		}
		req->m_tablet_info.end_row()->fill(buf, size);
	}
	if (argc > 4) {		// has ip
		char *ip_str = argv[4];
		unsigned int size = 0;
		while(*ip_str) { ++ip_str; ++size; }
		std::vector<char> ip(ip_str, ip_str + size);
		while(ip.size() < 32) {
			ip.push_back('-');
		}
		while(ip.size() > 32) {
			ip.pop_back();
		}
		req->m_server_address.ip()->fill(ip.data(), ip.size());
	}

	// header_wrapper::fields
	cout << "header_wrapper::" << endl;
	cout << " magic: " << hex << req->m_magic.get() << endl;
	cout << " version: " << hex << (int)req->m_version.get() << endl;
	cout << " type: " << hex << req->m_type.get() << endl;
	cout << " sequence_number: " << hex << req->m_sequence_number.get() << endl;
	cout << " data_length: " << hex << req->m_data_length.get() << endl;

	// request_wrapper::fields
	cout << "request_wrapper::" << endl;
	cout << " program_version: " << hex << req->m_program_version.get() << endl;

	// tablet_info_wrapper::fields
	cout << "tablet_info::" << endl;
	cout << " tablet_id: " << hex << req->m_tablet_info.tablet_id()->get() << endl;
	cout << " table_name: " << (std::string)*req->m_tablet_info.table_name() << endl;
	cout << " start_row: " << (std::string)*req->m_tablet_info.start_row() << endl;
	cout << " end_row: " << (std::string)*req->m_tablet_info.end_row() << endl;
	cout << " status: " << hex << req->m_tablet_info.status()->get() << endl;

	// server_address_wrapper::fields
	cout << "server_address_wrapper::" << endl;
	cout << " ip: " << hex << (std::string)*req->m_server_address.ip() << endl;
	cout << " port: " << hex << req->m_server_address.port()->get() << endl;
	cout << " startcode: " << hex << req->m_server_address.startcode()->get() << endl;

	return 0;
}
